package com.gitee.wsl.transform.validator.strings

import arrow.core.Either
import arrow.core.leftNel
import arrow.core.right
import com.gitee.wsl.transform.validator.ValidatorWithTransform
import com.gitee.wsl.transform.validator.transformEither
import kotlin.jvm.JvmName


/**
 * Narrows an existing String -> String [ValidatorWithTransform] by enforcing an exact length on the input string.
 *
 * For strings which have length that differs than the given [len] arguments, a failure is
 * returned with the error message generated by [ifError].
 *
 * @param len the max length, inclusive
 * @param ifError the error generating function
 *
 * @return valid if the input string is less than or equal to [len] or an invalid otherwise.
 */
fun <I, E> ValidatorWithTransform<I, String, E>.length(len: Int, ifError: (String) -> E): ValidatorWithTransform<I, String, E> =
   transformEither {
      when (it.length) {
         len -> it.right()
         else -> ifError(it).leftNel()
      }
   }

/**
 * Narrows an existing String -> String [ValidatorWithTransform] by enforcing an exact length on the input string.
 *
 * For strings which have length that differs than the given [len] arguments, a failure is
 * returned with the error message generated by [ifError].
 *
 * @param len the max length, inclusive
 * @param ifError the error generating function
 *
 * @return valid if the input string is less than or equal to [len] or an invalid otherwise.
 */
@JvmName("lengthOrNull")
fun <I, E> ValidatorWithTransform<I, String?, E>.length(len: Int, ifError: (String) -> E): ValidatorWithTransform<I, String?, E> =
   transformEither {
      when {
         it == null -> Either.Right(null)
         it.length == len -> it.right()
         else -> ifError(it).leftNel()
      }
   }

/**
 * Maps a string producing [ValidatorWithTransform] by enforcing a length on the string.
 *
 * The string is evaluated by the given function [f]. If this function returns false, then this parser
 * fails with an error generated by [ifError].
 *
 * @param f function to evaluate string length
 * @param ifError the error generating function
 *
 * @return valid if the input string has acceptable length or an invalid otherwise.
 */
fun <I, E> ValidatorWithTransform<I, String, E>.length(f: (Int) -> Boolean, ifError: (String) -> E): ValidatorWithTransform<I, String, E> =
   transformEither { if (f(it.length)) it.right() else ifError(it).leftNel() }

/**
 * Narrows an existing I -> String? [ValidatorWithTransform] by enforcing a max length on the input string.
 *
 * For strings which have length longer than the given [len] arguments, an invalid is
 * returned with the error message generated by [ifError].
 *
 * @param len the max length, inclusive
 * @param ifError the error generating function
 *
 * @return valid if the input string is less than or equal to [len] or an invalid otherwise.
 */
@JvmName("maxlenOrNull")
fun <I, E> ValidatorWithTransform<I, String?, E>.maxlen(len: Int, ifError: (String) -> E): ValidatorWithTransform<I, String?, E> =
   transformEither {
      when {
         it == null -> Either.Right(null)
         it.length > len -> ifError(it).leftNel()
         else -> it.right()
      }
   }

/**
 * Narrows an existing I -> String? [ValidatorWithTransform] by enforcing a max length on the input string.
 *
 * For strings which have length longer than the given [len] arguments, an invalid is
 * returned with the error message generated by [ifError].
 *
 * @param len the max length, inclusive
 * @param ifError the error generating function
 *
 * @return valid if the input string is less than or equal to [len] or an invalid otherwise.
 */
fun <I, E> ValidatorWithTransform<I, String, E>.maxlen(len: Int, ifError: (String) -> E): ValidatorWithTransform<I, String, E> =
   transformEither {
      when {
         it.length > len -> ifError(it).leftNel()
         else -> it.right()
      }
   }


/**
 * Narrows an existing I -> String [ValidatorWithTransform] by enforcing a min length on the input string.
 *
 * For strings which have length shorter than the given [len] arguments, an invalid is
 * returned with the error message generated by [ifError].
 *
 * @param len the max length, inclusive
 * @param ifError the error generating function
 *
 * @return valid if the input string is greater than or equal to [len] or an invalid otherwise.
 */
fun <I, E> ValidatorWithTransform<I, String, E>.minlen(len: Int, ifError: (String) -> E): ValidatorWithTransform<I, String, E> =
   transformEither {
      when {
         it.length < len -> ifError(it).leftNel()
         else -> it.right()
      }
   }

/**
 * Narrows an existing I -> String? [ValidatorWithTransform] by enforcing a min length on the input string.
 *
 * For strings which have length shorter than the given [len] arguments, an invalid is
 * returned with the error message generated by [ifError].
 *
 * @param len the max length, inclusive
 * @param ifError the error generating function
 *
 * @return valid if the input string is greater than or equal to [len] or an invalid otherwise.
 */
@JvmName("minlenOrNull")
fun <I, E> ValidatorWithTransform<I, String?, E>.minlen(len: Int, ifError: (String) -> E): ValidatorWithTransform<I, String?, E> =
   transformEither {
      when {
         it == null -> Either.Right(null)
         it.length < len -> ifError(it).leftNel()
         else -> it.right()
      }
   }
